package sf.r2dbc.mapper;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import sf.core.DBObject;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.ModelSql;
import sf.r2dbc.dao.R2dbcClient;
import sf.r2dbc.sql.R2dbcCrud;
import sf.r2dbc.sql.R2dbcUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * daoMapper实现类
 * @param <T>
 */
public class R2dbcDaoMapperImpl<T extends DBObject> implements R2dbcDaoMapper<T> {
    protected R2dbcClient r2dbcClient;
    protected Class<T> domainClass;

    public R2dbcDaoMapperImpl(Class<T> domainClass, R2dbcClient r2dbcClient) {
        this.domainClass = domainClass;
        this.r2dbcClient = r2dbcClient;
    }

    @Override
    public Mono<Integer> merge(T entity) {
        return getR2dbcClient().merge(entity);
    }

    @Override
    public Mono<Integer> insert(T entity) {
        return getR2dbcClient().insert(entity);
    }

    @Override
    public Flux<Integer> insertBatch(List<T> list) {
        return getR2dbcClient().batchInsert(list);
    }

    @Override
    public Flux<Integer> insertBatchFast(List<T> list) {
        return getR2dbcClient().batchInsertFast(list);
    }

    @Override
    public Mono<Integer> updateById(T entity) {
        return getR2dbcClient().update(entity);
    }

    @Override
    public Mono<Integer> update(T entity) {
        return getR2dbcClient().update(entity);
    }

    @Override
    public Flux<Integer> updateBatch(List<T> list) {
        return getR2dbcClient().batchUpdate(list);
    }

    @Override
    public Mono<Integer> delete(T entity) {
        return getR2dbcClient().delete(entity);
    }

    @Override
    public Mono<Integer> deleteById(Object... key) {
        return getR2dbcClient().deleteByPrimaryKeys(domainClass, key);
    }

    @Override
    public Mono<Integer> logicDelete(T entity) {
        return getR2dbcClient().logicDelete(entity);
    }

    @Override
    public Mono<Integer> logicDeleteById(Object... key) {
        return getR2dbcClient().logicDeleteByPrimaryKeys(domainClass, key);
    }

    @Override
    public Flux<Integer> deleteInBatch(Iterable<T> entities) {
        return getR2dbcClient().batchDelete(asList(entities));
    }

    @Override
    public Mono<Integer> deleteAllInBatch() {
        return getR2dbcClient().truncate(domainClass);
    }

    @Override
    public Mono<T> unique(Object... keys) {
        return getR2dbcClient().selectByPrimaryKeys(domainClass, keys);
    }

    @Override
    public Mono<T> single(T query) {
        return getR2dbcClient().selectOne(query);
    }

    @Override
    public Mono<T> lock(T query) {
        return getR2dbcClient().selectOneForUpdate(query);
    }

    @Override
    public Mono<Long> count() {
        return getR2dbcClient().monoFunc(r2dbcConnection -> {
            DBDialect dbDialect = R2dbcUtils.getDBDialect(r2dbcConnection);
            String sql = ModelSql.getSelectCountSql(dbDialect, domainClass);
            return R2dbcCrud.getInstance().getCrudSql().selectOne(r2dbcConnection, Long.class, sql).defaultIfEmpty(0L);
        });
    }

    @Override
    public Mono<Long> count(T query) {
        return getR2dbcClient().selectCount(query);
    }

    @Override
    public Mono<Boolean> existsById(Object... id) {
        return getR2dbcClient().existsByPrimaryKeys(domainClass, id);
    }

    @Override
    public Mono<Boolean> exists(T query) {
        return getR2dbcClient().exists(query);
    }

    @Override
    public Flux<T> selectList(T query) {
        return getR2dbcClient().selectList(query);
    }

    private <S> List<S> asList(Iterable<S> entities) {
        List<S> list = new ArrayList<>();
        for (S entity : entities) {
            list.add(entity);
        }
        return list;
    }

    @Override
    public R2dbcClient getR2dbcClient() {
        return r2dbcClient;
    }

    @Override
    public Class<T> getDomainClass() {
        return domainClass;
    }
}
